﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Xml.Linq;
using System.Xml.XPath;

// 引入Assemblies - System.Runtime.Serialization
// using System.Runtime.Serialization.Json;

// TODO Step 1 修改命名空间
namespace QRCodeCreator
{
    // V 1.0.3 - 2023-05-06 10:44:50
    // 修改 Gitee 获取版本号的代码

    // V 1.0.2 - 2022-02-07 15:50:27
    // 搞清楚了弹出 WerFault.exe 的问题, 要先判断 System.Diagnostics.Debugger.IsAttached == true 之后才能, System.Diagnostics.Debugger.Break()
    //    
    // V 1.0.1 - 2022-02-07 14:12:34
    // 没有网络时访问 GitXxx 获取最新版本时, 会弹出 WerFault.exe 并提示 XXXX.exe 已停止工作.
    // 优化没有网络的表现

    /// <summary>
    /// Interaction logic for UcCheckUpdate_GitXxx.xaml
    /// </summary>
    public partial class UcCheckUpdate_GitXxx : UserControl
    {
        // TODO Step2 选择 Gitee or Github, 并根据各自的要求修改发行版地址

        /// <summary>
        /// Gitee 发行版页面, 返回html信息
        /// </summary>
        public const string CheckUpdateUrl = "https://gitee.com/Howesdomo/QRCodeTools/releases";

        /// <summary>
        /// Github 发行版 api, 返回json信息
        /// </summary>
        public const string CheckUpdateUrl_Github = "https://api.github.com/repos/howesdomo/QRCodeTools/releases/latest";

        System.ComponentModel.BackgroundWorker mBgWorker_CheckUpdateGitee { get; set; }

        System.ComponentModel.BackgroundWorker mBgWorker_DownloadUpdate { get; set; }

        #region [DP] FindUpdateVersionInfo 

        public static readonly DependencyProperty FindUpdateVersionInfoProperty = DependencyProperty.Register
        (
            name: "FindUpdateVersionInfo",
            propertyType: typeof(string),
            ownerType: typeof(UcCheckUpdate_GitXxx),
            validateValueCallback: null, // new ValidateValueCallback((toValidate) => { return true; }),
            typeMetadata: new PropertyMetadata
            (
                defaultValue: null,
                propertyChangedCallback: null,
                coerceValueCallback: null
            )
        );

        public string FindUpdateVersionInfo
        {
            get { return (string)GetValue(FindUpdateVersionInfoProperty); }
            set { SetValue(FindUpdateVersionInfoProperty, value); }
        }

        #endregion

        #region [DP] InfoVisibility


        public static readonly DependencyProperty InfoVisibilityProperty = DependencyProperty.Register
        (
            name: "InfoVisibility",
            propertyType: typeof(Visibility),
            ownerType: typeof(UcCheckUpdate_GitXxx),
            validateValueCallback: null,
            typeMetadata: new PropertyMetadata
            (
                defaultValue: Visibility.Collapsed,
                propertyChangedCallback: null,
                coerceValueCallback: null
            )
        );

        public Visibility InfoVisibility
        {
            get { return (Visibility)GetValue(InfoVisibilityProperty); }
            set { SetValue(InfoVisibilityProperty, value); }
        }

        #endregion

        #region [DP] ProgressBarVisibility

        public static readonly DependencyProperty ProgressBarVisibilityProperty = DependencyProperty.Register
        (
            name: "ProgressBarVisibility",
            propertyType: typeof(Visibility),
            ownerType: typeof(UcCheckUpdate_GitXxx),
            validateValueCallback: null, // new ValidateValueCallback((toValidate) => { return true; }),
            typeMetadata: new PropertyMetadata
            (
                defaultValue: Visibility.Collapsed,
                propertyChangedCallback: null,
                coerceValueCallback: null
            )
        );

        public Visibility ProgressBarVisibility
        {
            get { return (Visibility)GetValue(ProgressBarVisibilityProperty); }
            set { SetValue(ProgressBarVisibilityProperty, value); }
        }

        #endregion


        public UcCheckUpdate_GitXxx()
        {
            InitializeComponent();
            this.Loaded += ucLoaded;
            this.btnCheckUpdate.Click += btnCheckUpdate_Click;
        }

        void btnCheckUpdate_Click(object sender, RoutedEventArgs e)
        {
            checkUpdate();
        }

        void ucLoaded(object sender, RoutedEventArgs e)
        {
            checkUpdate();
        }

        int mRunCheckUpdateCount { get; set; } = 0;

        void checkUpdate()
        {
            if (mBgWorker_CheckUpdateGitee != null && mBgWorker_CheckUpdateGitee.IsBusy == true)
            {
                return;
            }

            mBgWorker_CheckUpdateGitee = new System.ComponentModel.BackgroundWorker();
            mBgWorker_CheckUpdateGitee.DoWork += (bgSender, bgArgs) =>
            {
                if (mRunCheckUpdateCount == 0)
                {
                    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
                }
                bgArgs.Result = CheckUpdateUtils.CheckUpdateGitee(UcCheckUpdate_GitXxx.CheckUpdateUrl);
                // bgArgs.Result = CheckUpdateUtils.CheckUpdateGithub(UcCheckUpdate_GitXxx.CheckUpdateUrl_Github);
            };

            mBgWorker_CheckUpdateGitee.RunWorkerCompleted += (bgSender, bgResult) =>
            {
                mRunCheckUpdateCount++;

                if (bgResult.Error != null)
                {
                    MessageBox.Show(bgResult.Error.Message, "捕获异常");
                }
                else
                {
                    if (bgResult.Result == null)
                    {
                        return; // 1.没有网络; 2.GitXxx网址填写错误
                    }

                    this.btnCheckUpdate.Visibility = Visibility.Collapsed;

                    var hasNewVersion = (bool)bgResult.Result;
                    if (hasNewVersion == true)
                    {
                        this.InfoVisibility = Visibility.Visible;
                        this.FindUpdateVersionInfo = $"发现新版本: {CheckUpdateUtils.CheckUpdateModel.Version}";
                    }
                }
            };

            mBgWorker_CheckUpdateGitee.WorkerReportsProgress = true;
            mBgWorker_CheckUpdateGitee.ProgressChanged += (bgSender, bgArgs) =>
            {

            };

            mBgWorker_CheckUpdateGitee.RunWorkerAsync(new object[] { });
        }

        private void Hyperlink_Click(object sender, RoutedEventArgs e)
        {
            if (this.btnCheckUpdate.Visibility == Visibility.Visible)
            {
                return;
            }

            if (mBgWorker_DownloadUpdate != null && mBgWorker_DownloadUpdate.IsBusy == true)
            {
                return;
            }

            var dr = MessageBox.Show
            (
                owner: Application.Current.MainWindow,
                messageBoxText: $"{FindUpdateVersionInfo}\r\n确认更新?",
                caption: "提示",
                button: MessageBoxButton.OKCancel,
                icon: MessageBoxImage.Question,
                defaultResult: MessageBoxResult.OK
            );

            if (dr != MessageBoxResult.OK)
            {
                return;
            }

            mBgWorker_DownloadUpdate = new System.ComponentModel.BackgroundWorker();
            mBgWorker_DownloadUpdate.DoWork += (bgSender, bgArgs) =>
            {
                CheckUpdateUtils.DownloadUpdate(CheckUpdateUtils.CheckUpdateModel.Version, CheckUpdateUtils.CheckUpdateModel.DownloadAddress);
            };

            mBgWorker_DownloadUpdate.RunWorkerCompleted += (bgSender, bgResult) =>
            {
                if (bgResult.Error != null)
                {
                    MessageBox.Show(bgResult.Error.Message, "捕获异常", MessageBoxButton.OK, MessageBoxImage.Error);
                }
                else
                {
                    if (System.IO.File.Exists(CheckUpdateUtils.CheckUpdateModel.ZipFilePath) == true)
                    {
                        if (CheckUpdateUtils.HasAppExit_Handle == true)
                        {
                            App.Current.Exit -= CheckUpdateUtils.Current_Exit;
                        }

                        this.ProgressBarVisibility = Visibility.Collapsed;
                        this.InfoVisibility = Visibility.Collapsed;
                        CheckUpdateUtils.CheckUpdateModel.IsDownloaded = true;
                        App.Current.Exit += CheckUpdateUtils.Current_Exit;
                        CheckUpdateUtils.HasAppExit_Handle = true;
                    }

                    MessageBox.Show($"新版本{CheckUpdateUtils.CheckUpdateModel.Version}下载完毕，\r\n关闭程序后更新到新版。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
                }
            };

            mBgWorker_DownloadUpdate.WorkerReportsProgress = true;
            mBgWorker_DownloadUpdate.ProgressChanged += (bgSender, bgArgs) =>
            {

            };

            this.FindUpdateVersionInfo = $"下载新版本: {CheckUpdateUtils.CheckUpdateModel.Version}";
            this.ProgressBarVisibility = Visibility.Visible;
            mBgWorker_DownloadUpdate.RunWorkerAsync(new object[] { });
        }
    }

    public class CheckUpdateUtils
    {
        /// <summary>
        /// 检查更新数据类
        /// </summary>
        public static CheckUpdateModel CheckUpdateModel { get; set; }

        /// <summary>
        /// 用于判断有否注册了 Application.Exit 事件
        /// </summary>
        public static bool HasAppExit_Handle { get; set; }

        public static IWebProxy WebHelper_GetProxy()
        {
            return null;
        }

        public static bool? CheckUpdateGitee(string url)
        {
            try
            {
                // Util.Web.WebUtils.FixWebRequest(); // 已迁移到 App.Startup 中处理

                var request = (HttpWebRequest)WebRequest.Create(url);
                request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393";
                request.Proxy = WebHelper_GetProxy();

                // var response = (HttpWebResponse)await request.GetResponseAsync();
                var response = (HttpWebResponse)request.GetResponse();
                using (var resultStream = response.GetResponseStream())
                {
                    if (resultStream == null)
                        return null;

                    using (var reader = new StreamReader(resultStream))
                    {
                        // var result = await reader.ReadToEndAsync();						
                        string html = reader.ReadToEnd();

                        string latestVersionStr = getVerionStr4Gitee(html); // 获取 gitee 上最新版本号

                        if (latestVersionStr.IsNullOrEmpty()) { return false; }

                        var version = Version.Parse(latestVersionStr);

                        // 比较版本号是否需要更新
                        if (version <= Assembly.GetExecutingAssembly().GetName().Version) { return false; }

                        string giteeDownloadUrl = string.Empty;
                        #region 获取 giteeDownloadUrl

                        // TODO优化直接获取到 下载地址
                        var pattern = "(?<=<div class='ui celled list releases-download-list'>\n<div class='item'>\n)<a href=\"[A-Za-z0-9_./]{3,}\">";
                        var a_href_MatchCollection = Regex.Matches(html, pattern);

                        string a_href = a_href_MatchCollection[0].Value;
                        string str_href = "href=\"";
                        string relativeUrl = a_href.Substring(a_href.IndexOf(str_href));
                        giteeDownloadUrl = relativeUrl.Substring(str_href.Length, relativeUrl.Length - 2 - str_href.Length);

                        #endregion

                        #region [弃用]获取 SHA 信息 (由于网站的 SHA 值是指 git 提交的SHA, 并不是下载文件的SHA )

                        //string SHA = string.Empty;

                        //var patternSHA = "(?<=<a class=\"download-archive\" data-ref=\"1.0.0.12\" data-sha=\")[0-9a-f]{40}";
                        //var sha_MatchCollection = Regex.Matches(html, patternSHA);
                        //SHA = sha_MatchCollection[0].Value;

                        #endregion

                        string update_download_path = $"https://gitee.com/{giteeDownloadUrl}"; // 拼接新版本下载更新包

                        CheckUpdateUtils.CheckUpdateModel = new CheckUpdateModel();
                        CheckUpdateUtils.CheckUpdateModel.Version = version;
                        CheckUpdateUtils.CheckUpdateModel.DownloadAddress = update_download_path;
                    }
                }

                return true;
            }
            catch (Exception ex)
            {
#if DEBUG
                string msg = $"{ex}";
                System.Diagnostics.Debug.WriteLine(msg);
                if (System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debugger.Break();
#endif

                return null;
            }
            finally
            {
                GC.Collect();
            }
        }

        public static bool? CheckUpdateGithub(string url)
        {
            try
            {
                // Util.Web.WebUtils.FixWebRequest(); // 已迁移到 App.Startup 中处理

                var request = (HttpWebRequest)WebRequest.Create(url);
                request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393";
                request.Proxy = WebHelper_GetProxy();

                // var response = (HttpWebResponse)await request.GetResponseAsync();
                var response = (HttpWebResponse)request.GetResponse();
                using (var resultStream = response.GetResponseStream())
                {
                    if (resultStream == null)
                        return null;

                    using (var reader = new StreamReader(resultStream))
                    {
                        // var result = await reader.ReadToEndAsync();
                        var result = reader.ReadToEnd();
                        var jsonReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(result), new System.Xml.XmlDictionaryReaderQuotas());
                        var release = XElement.Load(jsonReader);

                        var xDoc = XDocument.Parse(release.ToString());
                        var queryVersion = from i in xDoc.Descendants("tag_name") select i.Value;

                        if (queryVersion.Count() <= 0)
                            return false;

                        var version = Version.Parse(queryVersion.First());

                        if (version.Major == 0 || version <= Assembly.GetExecutingAssembly().GetName().Version)
                            return true;

                        var queryUrl = from i in xDoc.Descendants("browser_download_url") select i.Value;
                        // 获取下载发行版的 Url
                        string update_download_path = queryUrl.First();

                        CheckUpdateUtils.CheckUpdateModel = new CheckUpdateModel();
                        CheckUpdateUtils.CheckUpdateModel.Version = version;
                        CheckUpdateUtils.CheckUpdateModel.DownloadAddress = update_download_path;
                    }
                }

                return true;
            }
            catch (Exception ex)
            {
#if DEBUG
                string msg = $"{ex}";
                System.Diagnostics.Debug.WriteLine(msg);
                if (System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debugger.Break();
#endif
                return null;
            }
            finally
            {
                GC.Collect();
            }
        }

        public static void Current_Exit(object sender, System.Windows.ExitEventArgs e)
        {
            InstallUpdate(CheckUpdateUtils.CheckUpdateModel.ZipFilePath);
        }

        /// <summary>
        /// 下载更新包
        /// </summary>
        /// <param name="url"></param>
        public static void DownloadUpdate(Version version, string url)
        {
            string fileName = Path.GetFileName(url);

            string saveDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "HoweSoftware", "QRCodeCreator", "Downloads", version.ToString());


            if (Directory.Exists(saveDir) == false) { Directory.CreateDirectory(saveDir); }

            string savePath = Path.Combine(saveDir, fileName);
            if (File.Exists(savePath) == true) { File.Delete(savePath); }

            CheckUpdateUtils.CheckUpdateModel.ZipFilePath = savePath;

            using (var webClient = new WebClient())
            {
                webClient.Credentials = CredentialCache.DefaultNetworkCredentials;
                webClient.Headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393";
                webClient.Proxy = WebHelper_GetProxy();
                webClient.DownloadFile(address: new Uri(url), fileName: savePath);
            }
        }

        /// <summary>
        /// <para>执行 UpdateSoon.exe</para>
        /// <para>1. 解压zip</para>
        /// <para>2. 拷贝zip内所有文件到程序目录</para>
        /// </summary>
        public static void InstallUpdate(string zipFilePath)
        {
            string an = Assembly.GetExecutingAssembly().GetName().Name;
            string[] args = new string[3] { an, zipFilePath, Environment.CurrentDirectory };
            Util.ProgressUtils.ExcuteProgressWithArguments("UpdateSoon.exe", args.CombineString(" ", false));
        }

        private static void WebClient_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {

            string msg = $"downloaded ...";
            System.Diagnostics.Debug.WriteLine(msg);

            System.Diagnostics.Debugger.Break();

        }

        static string getVerionStr4Gitee(string html)
        {
            string r = null;

            string tagSymbol = "<div class='release-tag-item'>";

            var matchCollection = Regex.Matches(html, tagSymbol);

            if (matchCollection.Count <= 0)
            {
                return r; // 没有版本
            }

            // int start = html.IndexOf("<div class='tag-name'>");
            int start = html.IndexOf("<div class='tag-name' data-tag-name='"); // 2023-05-06 10:40:42 改版了 -- <div class='tag-name' data-tag-name='1.0.0.17'>

            string content = html.Substring(start);

            string startContent = "<span>";
            string endContent = "</span>";

            int startSpan = content.IndexOf(startContent);
            int endSpan = content.IndexOf(endContent);

            r = content.Substring(startSpan + startContent.Length, endSpan - startSpan - startContent.Length);

            return r;

        }
    }

    /// <summary>
    /// 检查更新数据类
    /// </summary>
    public class CheckUpdateModel
    {
        /// <summary>
        /// 发布版在 Gitee / Github 上发现的最新版本
        /// </summary>
        public Version Version { get; set; }

        /// <summary>
        /// 发布版在 Gitee / Github 下载地址
        /// </summary>
        public string DownloadAddress { get; set; }

        /// <summary>
        /// 下载zip文件在硬盘的位置
        /// </summary>
        public string ZipFilePath { get; set; }

        /// <summary>
        /// zip文件下载完毕
        /// </summary>
        public bool IsDownloaded { get; set; }
    }
}